#include "threads.h"
#include "error.h"

pthread_t elog_thrd_main;
pthread_t elog_thrd_scnd;

pthread_mutex_t global_mutex_lock;
int global_mutex_owner = 0; //the thread owning the lock.
int thrd_locks [] = {0, 0, 0};


int elog_thrd_finger()
{
#ifndef NO_THREADS
	return 1;
#else
	return 0;
#endif
}

void __launch_func(struct elog_thread *th)
{
	th->state = ELOG_THRD_STATE_BUSY;

	th->func(th->thread_data);

	th->state = ELOG_THRD_STATE_DONE;
	// pthread_exit(NULL);
}


void __launch_func0(struct elog_thread *th)
{
	th->state = ELOG_THRD_STATE_BUSY;

	th->func0();

	th->state = ELOG_THRD_STATE_DONE;
	//pthread_exit(NULL);
}

void elog_thrd_initialize()
{
  elog_thrd_main = pthread_self();
  if (pthread_mutex_init(&global_mutex_lock, NULL)) {
	elog_err_print("The mutex init failed\n");
	exit(1);
  }
}


void elog_thrd_finish(struct elog_thread *th, int patience)
{
#ifndef WIN32
	while (th->state == ELOG_THRD_STATE_BUSY)
		usleep(patience);
#else
	while (th->state == ELOG_THRD_STATE_BUSY);	//poor NT users
#endif
}

struct elog_thread *elog_thrd_launch(void *(*func) (void *),
				     void *func_data)
{
	struct elog_thread *th = elog_thr_thread_new();
	pthread_mutex_init(&(th->mutex), NULL);
	th->thread_data = func_data;
	th->func = func;
	pthread_create(&(th->sysThread), NULL,
		       (void *(*)(void *)) __launch_func, th);
	//__launch_func(th);
	pthread_detach(th->sysThread);
	return th;
}
struct elog_thread *elog_thrd_launch0(void *(*func) (void))
{
	struct elog_thread *th = elog_thr_thread_new();
	pthread_mutex_init(&(th->mutex), NULL);
	th->thread_data = NULL;
	th->func0 = func;
	pthread_create(&(th->sysThread), NULL,
		       (void *(*)(void *)) __launch_func0, th);
	//__launch_func(th);
	pthread_detach(th->sysThread);
	return th;
}

struct elog_thread *elog_thr_thread_new()
{
	return malloc(sizeof(struct elog_thread));
}
void elog_thr_thread_free(struct elog_thread *th)
{
	if (th) {
		if (th->thread_data)
			free(th->thread_data);
		free(th);
	}
}
void elog_thrd_exit(struct elog_thread *th)
{
	pthread_cancel(th->sysThread);
}
void elog_thrd_lock(struct elog_thread *th)
{
	pthread_mutex_lock(&(th->mutex));	
}


void elog_thrd_unlock(struct elog_thread *th)
{
	pthread_mutex_unlock(&(th->mutex));	
}


int elog_thrd_trylock(struct elog_thread *th)
{
	return pthread_mutex_trylock(&(th->mutex));	
}



int elog_thrd_identify_me()
{
     if (pthread_equal(pthread_self(), elog_thrd_main))
          return ELOG_THREAD_MAIN;
     if (pthread_equal(pthread_self(), elog_thrd_scnd))
          return ELOG_THREAD_SECOND;
     return ELOG_THREAD_OTHER;
}
void elog_glob_reg_scnd_thread()
{
     elog_thrd_scnd = elog_thrd_identify_me();
}

void elog_glob_lock()
{
     int me = elog_thrd_identify_me();
     if (thrd_locks[me]) {
          (thrd_locks[me])++;
          return;
     }
     thrd_locks[me] = 1;
     if (pthread_mutex_lock(&global_mutex_lock)) {
          elog_err_print("couldn't lock the mutex\n");
          exit(1);
     }
     global_mutex_owner = me;
}
void elog_glob_unlock()
{
     int me = elog_thrd_identify_me();
     (thrd_locks[me])--;
     if (thrd_locks[me] <= 0) {
          if (me == global_mutex_owner)
               pthread_mutex_unlock(&global_mutex_lock);
          thrd_locks[me] = 0;
     }
}

